
#define _CRT_SECURE_NO_DEPRECATE

#include "bmp2g9b.h"
#include <iostream>
#include "compress.h"
#include <stdio.h>
#include <time.h>
using namespace std;

/*************************************************************/
/* Compress a G9B file                                       */
/*************************************************************/

bool Compress(char* fileName, DWORD dataSize)
{
	FILE*       file;
	G9B_HEADER  g9bHeader;
	char*       pStr=NULL;
	BYTE*       buffer=NULL, *palette=NULL;
	char*       fileout=NULL;
	int         compressed_size;

	file=fopen(fileName,"rb");
	if(file == NULL)
	{
		cout << "*** Error opening file" << endl;
		return false;
	}
	fread(&g9bHeader,1,sizeof(G9B_HEADER),file);
	if ( g9bHeader.info.nrColors )
	{
		palette = (BYTE*)malloc(g9bHeader.info.nrColors * 3);
       	fread(palette,1,g9bHeader.info.nrColors * 3,file);
	}

	buffer = (BYTE*)malloc(dataSize);
	fread(buffer,1,dataSize,file); 
    fclose(file);
	pStr=strrchr(fileName,'.');
	strcpy(pStr,".RAW");

	fileout = (char*)malloc(strlen(fileName)+1);
	strcpy(fileout, fileName);
	pStr=strrchr(fileout,'.');
	strcpy(pStr,".PCK");

    file=fopen(fileName,"wb");
	fwrite(buffer,1,dataSize,file);
    fclose(file);
	free(buffer);

	compressed_size = compress(fileName, fileout, 16000);
	
	buffer = (BYTE*)malloc(compressed_size);
    file=fopen(fileout,"rb");
	if(file==NULL) return false;
	fread(buffer,1,compressed_size,file);
    fclose(file);

	remove( fileName );
	remove( fileout );

	g9bHeader.info.compression = 1;
	g9bHeader.info.dataSize[0] = (BYTE)compressed_size & 255;
	g9bHeader.info.dataSize[1] = (BYTE)(compressed_size >> 8) & 255;
	g9bHeader.info.dataSize[2] = (BYTE)(compressed_size >> 16) & 255;

	pStr=strrchr(fileName,'.');
	strcpy(pStr,".G9B");
	
    file=fopen(fileName,"wb");
    fwrite(&g9bHeader,1,sizeof(G9B_HEADER),file);
	if ( g9bHeader.info.nrColors )
	{
		fwrite(palette,1, g9bHeader.info.nrColors * 3,file);
		free(palette);
	}	
	fwrite(buffer,1,compressed_size,file);
    fclose(file);

	free(buffer);
	return true;
}
/*************************************************************/
/* Generate patterns functions                               */
/*************************************************************/

void MakePatterns(BYTE* pG9bData,DWORD width,DWORD height,BYTE bitDepth,G9B_SETTINGS* g9bSettings)
{
   BYTE*	  temp=NULL,*buffer=NULL;
   BYTE*      pLine;
   DWORD      patX = g9bSettings->patX;
   DWORD      patY = g9bSettings->patY;

   buffer =(BYTE*)malloc((width*height*bitDepth)>>3);
   temp = buffer;
   for (DWORD nrYpat=0;nrYpat<(height/patY);nrYpat++)
   { 
      for (DWORD nrXpat=0;nrXpat<(width/patX);nrXpat++)
	  {
        pLine=pG9bData+((((width*(nrYpat*patY))+(nrXpat*patX))*bitDepth)>>3);
        for(DWORD y=0;y<patY;y++) 
		{
	       memcpy(temp,pLine,(patX*bitDepth)>>3);
	       temp+=(patX*bitDepth)>>3;
	       pLine+=(width*bitDepth)>>3;
		}
	  }
   }
   memcpy(pG9bData,buffer,(width*height*bitDepth)>>3);
   free(buffer);
}
/*************************************************************/
/* Conversion routines 1 bit -> 2 bit                        */
/*************************************************************/
void Convert1To2Bit(DWORD width, DWORD height,BYTE* pBitmap,BYTE* pG9bData)
{
	int  pixelCnt;
	int  bytePixels; 
	BYTE temp;
	BYTE tempResult=0;
    int  resultCnt=0;
	
	for(DWORD line=0;line<height;line++)
	{
		pixelCnt=width;
		while(pixelCnt)
		{
			temp=*pBitmap++;
			if((pixelCnt-8)>=0)
			{
				pixelCnt-=8;
				bytePixels=8;
			}
			else bytePixels=pixelCnt;
			for(int i=0;i<bytePixels;i++)
			{
				tempResult<<=2;
				if(temp&(1<<(7-i))) tempResult|=0x01;
				resultCnt++; 
				if((resultCnt==4)|| (bytePixels<4))
				{
					*pG9bData++=tempResult;
					tempResult=0;
					resultCnt=0;
				}
			}	
		}
	}
}

/*************************************************************/
/* Conversion routines 8 bit -> 8 bit 64 color               */
/*************************************************************/

bool Convert256To64Color(DWORD width, DWORD height,BYTE* pBitmap,BYTE* pG9bData)
{
   bool retVal=true;
   DWORD nrPixels=height*width;
   for (DWORD pixel=0;pixel<nrPixels;pixel++)
   {
		if(*pBitmap<64)
		{
			*pG9bData=*pBitmap;
		}
		else
		{
			retVal=false;
			*pG9bData=0;
		}
		pG9bData++;
		pBitmap++;
   }
   return retVal;
}

/*************************************************************/
/* Conversion routines 24 bit                                */
/*************************************************************/
void Convert24To15Bit(DWORD width, DWORD height,BYTE* pBitmap,BYTE* pG9bData)
{
   BYTE b,g,r,b15,g15,r15;
   DWORD nrPixels=height*width;
   for (DWORD pixel=0;pixel<nrPixels;pixel++)
   {
         b=*pBitmap++;
         g=*pBitmap++;
         r=*pBitmap++;
         b15=b>>3;
		 if ( ((b&7)>3) && (b<31) ) b15++;
         g15=g>>3;
	 	 if ( ((g&7)>3) && (g<31) ) g15++;
         r15=r>>3;
	 	 if ( ((r&7)>3) && (r<31) ) r15++;

         *pG9bData++=b15|(r15<<5);
	     *pG9bData++=(g15<<2)|(r15>>3);
   }
}

void Convert24To8BitYUV411(DWORD width, DWORD height,BYTE* pBitmap,BYTE* pG9bData)
{
	int r,g,b;
	int y0,y1,y2,y3,v,u;
	BYTE   tU,tV,tY;
    DWORD nrPixels=height*width>>2;
	// Get the UV from for pixels and average it
	for (DWORD pixel=0;pixel<nrPixels;pixel++)
    {
		b=*pBitmap++;
		g=*pBitmap++;
		r=*pBitmap++;

  		y0 = (g>>1) + (r>>2) + (b>>3);
		u  = r-y0;
		v  = b-y0;

		b=*pBitmap++;
		g=*pBitmap++;
		r=*pBitmap++;

		y1 = (g>>1) + (r>>2) + (b>>3);
		u += r-y1;
		v += b-y1;

		b=*pBitmap++;
		g=*pBitmap++;
		r=*pBitmap++;

    	y2 = (g>>1) + (r>>2) + (b>>3);
		u += r-y2;
		v += b-y2;

		b=*pBitmap++;
		g=*pBitmap++;
		r=*pBitmap++;

		y3 = (g>>1) + (r>>2) + (b>>3);
		u += r-y3;
		v += b-y3;

		// calculate average 
		v>>=3; 
		u>>=3;
		tV=((BYTE)v)>>2;
		if ((tV<63) &&(((BYTE)v&3)>2)) tV++;
		tU=((BYTE)u)>>2;
		if ((tU<63) &&(((BYTE)u&3)>2)) tU++;

		tY=((BYTE)y0)&0xF8;
		if ((tY<248) && (((BYTE)y0&7)>4)) tY+=8;
		*pG9bData++=tY|(tV&0x7);

		tY=((BYTE)y1)&0xF8;
		if ((tY<248) &&(((BYTE)y1&7)>4)) tY+=8;
		*pG9bData++=tY|(tV>>3);

		tY=((BYTE)y2)&0xF8;
		if ((tY<248) && (((BYTE)y2&7)>4)) tY+=8;
        *pG9bData++=tY|(tU&0x7);

		tY=((BYTE)y3)&0xF8;
		if ((tY<248) &&(((BYTE)y3&7)>4)) tY+=8;
        *pG9bData++=tY|(tU>>3);
	}
}

void Convert24To8BitYJK411(DWORD width, DWORD height,BYTE* pBitmap,BYTE* pG9bData)
{
	DWORD r,g,b;
	DWORD y0,y1,y2,y3,j,k;
	BYTE   tJ,tK,tY;
    DWORD nrPixels=height*width>>2;
	// Get the UV from for pixels and averidge it
	for (DWORD pixel=0;pixel<nrPixels;pixel++)
    {
		b=*pBitmap++;
		g=*pBitmap++;
		r=*pBitmap++;

  		y0 = (b>>1) + (r>>2) + (g>>3);
		j  = r-y0;
		k  = g-y0;

		b=*pBitmap++;
		g=*pBitmap++;
		r=*pBitmap++;

		y1 = (b>>1) + (r>>2) + (g>>3);
		j += r-y1;
		k += g-y1;

		b=*pBitmap++;
		g=*pBitmap++;
		r=*pBitmap++;

    	y2 = (b>>1) + (r>>2) + (g>>3);
		j += r-y2;
		k += g-y2;


		b=*pBitmap++;
		g=*pBitmap++;
		r=*pBitmap++;

		y3 = (b>>1) + (r>>2) + (g>>3);
		j += r-y3;
		k += g-y3;

		// calculate average
		k/=8;
		j/=8;
		
		tK=((BYTE)k)>>2;
		if ((tK<63) &&(((BYTE)k&3)>2)) tK++;
		tJ=((BYTE)j)>>2;
		if ((tJ<63) &&(((BYTE)j&3)>2)) tJ++;

		tY=((BYTE)y0)&0xF8;
		if ((tY<248) && (((BYTE)y0&7)>4)) tY+=8;
		*pG9bData++=tY|(tK&0x7);

		tY=((BYTE)y1)&0xF8;
		if ((tY<248) &&(((BYTE)y1&7)>4)) tY+=8;
		*pG9bData++=tY|(tK>>3);

		tY=((BYTE)y2)&0xF8;
		if ((tY<248) && (((BYTE)y2&7)>4)) tY+=8;
        *pG9bData++=tY|(tJ&0x7);

		tY=((BYTE)y3)&0xF8;
		if ((tY<248) &&(((BYTE)y3&7)>4)) tY+=8;
        *pG9bData++=tY|(tJ>>3);
	}
}

/*************************************************************/
/* BMP load                                                  */
/*************************************************************/

bool LoadImage(char* fileName, BYTE **pData,BYTE** pPalleteData,BYTE* pBitDepth,BYTE* pNrColors,DWORD* pWidth ,DWORD* pHeight)
{
	DWORD			 nrColors;
	bool             status;
	DWORD			 bmLineSize;
	DWORD            lineSize;
	FILE*            pFile;
	BITMAPFILEHEADER bmHeader={0};
	BITMAPINFOHEADER bmpi={0};
	RGBQUAD          bmpPalette[256];
	unsigned char*   pImageBuffer;
	
	if ((fileName == NULL) || (pData == NULL) || (pBitDepth==NULL) || (pWidth==NULL) || (pHeight==NULL)) return false;
	
	pFile = fopen(fileName, "rb");
	if (pFile==NULL)
	{
		cout << "*** Coudln't open file" << endl;
		return false;
	}
	*pData = NULL;

    fread(&bmHeader,1,sizeof(BITMAPFILEHEADER),pFile);
	if(strncmp((char*)&bmHeader.bfType,"BM",2)==0)
	{
		fread(&bmpi,1,sizeof(BITMAPINFOHEADER),pFile);
		*pBitDepth = (BYTE)bmpi.biBitCount;
		*pWidth    = bmpi.biWidth;
		*pHeight   = bmpi.biHeight;

		// Load palette and convert it to 15bit palette
		if(bmpi.biBitCount<16)
		{
		   DWORD j=0;
		   if(bmpi.biClrUsed==0)
		   {
			   bmpi.biClrUsed=1<<bmpi.biClrUsed;
		   }
			fread(&bmpPalette,1,bmpi.biClrUsed * sizeof(RGBQUAD),pFile);
		   // Never more then 64 colors are converted
		   if(bmpi.biClrUsed>64) nrColors=64;
		   else nrColors=bmpi.biClrUsed;
		   BYTE* pPalette = new BYTE[nrColors*3];
		   for (DWORD i=0;i<nrColors;i++)
		   {
			  pPalette[j++]=bmpPalette[i].rgbRed>>3;
			  pPalette[j++]=bmpPalette[i].rgbGreen>>3;
			  pPalette[j++]=bmpPalette[i].rgbBlue>>3;
		   }
		   *pPalleteData = pPalette;
		   *pNrColors = (BYTE)nrColors;
		}
		else
		{
			*pPalleteData=NULL;
			*pNrColors=0;
		}
		// Allocate image buffer
		pImageBuffer     = new unsigned char[(bmpi.biWidth *  bmpi.biHeight * bmpi.biBitCount)/8];
		*pData           = pImageBuffer;
	
		// Calculate bmp line size
		bmLineSize=bmpi.biSizeImage/bmpi.biHeight;
		// Line size 'converted' image
		lineSize = (bmpi.biWidth * bmpi.biBitCount) / 8;
      
        // Calculate padding bytes  
		DWORD paddingBytes=lineSize%4;
		if(paddingBytes) paddingBytes=4-paddingBytes;

		// set file pointer on last line
		pImageBuffer+=(bmpi.biHeight-1)*lineSize;
		for (LONG line=0;line<bmpi.biHeight;line++)
		{
			DWORD data;
			fread(pImageBuffer,1,lineSize,pFile);
			fread(&data,1,paddingBytes,pFile);
			pImageBuffer-=lineSize;
		}
		status = true;
	}	
	else status = false;
	fclose(pFile);
	return status;
}
/*************************************************************/
/* Get roi                                                   */
/*************************************************************/


int Rnd(int amount)
{
   return (int)(  (rand() + time(NULL)) &amount)-amount/2+1;
}
int Limit(int value,int min, int max)
{
	if(value < min) return min;
	if(value > max) return max;
	return value;
}

#define MASK_GREEN  15
#define MASK_RED    15
#define MASK_BLUE   15


void DitherImage(G9B_SETTINGS* g9bSettings,BYTE* pImageData,DWORD width,DWORD height)
{
	DWORD nrPixels=height*width;
	for (DWORD pixel=0;pixel<nrPixels;pixel++)
    {
		*pImageData++=(BYTE)Limit(*pImageData+(Rnd(MASK_BLUE)  * rand()&3),0,255);
		*pImageData++=(BYTE)Limit(*pImageData+(Rnd(MASK_GREEN) * rand()&7),0,255);
		*pImageData++=(BYTE)Limit(*pImageData+(Rnd(MASK_RED)   * rand()&3),0,255);
	}
}

void GetRoi(G9B_SETTINGS* g9bSettings,BYTE** pImageData,DWORD& width,DWORD& height,BYTE bitDepth)
{
	BYTE* pSrc;
	BYTE* pDst;
	BYTE* pRoi= new BYTE[(g9bSettings->width * g9bSettings->height * bitDepth)>>3];
    DWORD roiLineSize = (g9bSettings->width*bitDepth)>>3;
	DWORD orgLineSize = (width*bitDepth)>>3;
	pDst = pRoi;
	pSrc=*pImageData + (((g9bSettings->left + g9bSettings->top * width) * bitDepth)>>3);

	for(DWORD y=0;y<g9bSettings->height;y++)
	{
		memcpy(pDst,pSrc,roiLineSize);
		pDst+=roiLineSize;
		pSrc+=orgLineSize;
	}
	delete *pImageData;
    *pImageData = pRoi;
	width  = g9bSettings->width;
	height = g9bSettings->height;
}

bool SubString(char* lpString, char* subString, long* lpStringOffset, char subchar)
{
	char* pend=(char*)strchr(&lpString[*lpStringOffset],subchar); 
	if (pend==NULL) 
	{
		strcpy(subString,&lpString[*lpStringOffset]);
	     *lpStringOffset+=strlen(subString);
		if(strlen(subString)>0) return true;
		return false;
	}	
	int length=pend-&lpString[*lpStringOffset];
    strncpy(subString,&lpString[*lpStringOffset],length);
    subString[length]=0;
    *lpStringOffset+=length+1;
	return true;
}


bool GetRoiSize(char* roiSize,DWORD* left,DWORD* top,DWORD* width, DWORD* height)
{
	long stringOffset=0;
	char subString[32];
  	if (strlen(roiSize)>0)
	{
	   if(SubString(roiSize,subString,&stringOffset,','))
	   {
		  *left=atoi(subString);
  	 	  if(SubString(roiSize,subString,&stringOffset,','))
		  {
		     *top=atoi(subString);
		   	 if(SubString(roiSize,subString,&stringOffset,','))
			 {
				*width=atoi(subString);	 
				if(SubString(roiSize,subString,&stringOffset,','))
				{
					*height=atoi(subString);
					return true;
				}
			 }
		  }
	   }
	}
	cout << "*** Missing parameter" << endl;
	return false;
}

/*************************************************************/
/* G9b routines                                              */
/*************************************************************/

FILE* CreateG9bHeader(char* fileName,BYTE bitDepth,BYTE colorType, BYTE nrColors,DWORD width,DWORD height,DWORD* pDataSize)
{
	FILE* file;
	G9B_HEADER g9bHeader;
    strncpy(g9bHeader.g9bID,"G9B",3);
	g9bHeader.headerSize=sizeof(G9B_INFO_HEADER);
	g9bHeader.info.bitDepth=bitDepth;
	g9bHeader.info.colorType=colorType;
	g9bHeader.info.nrColors=nrColors;
	g9bHeader.info.compression=0;
	g9bHeader.info.width=(WORD)width;
	g9bHeader.info.height=(WORD)height;
	*pDataSize=(width*height*bitDepth)>>3;
	g9bHeader.info.dataSize[0]=(BYTE)(*pDataSize)&0xff;
	g9bHeader.info.dataSize[1]=(BYTE)((*pDataSize)>>8)&0xff;
	g9bHeader.info.dataSize[2]=(BYTE)((*pDataSize)>>16)&0xff;
	file=fopen(fileName,"wb");
	if(file) fwrite(&g9bHeader,1,sizeof(G9B_HEADER),file);
	return file;
}

void ProcessImage(char* fileName,G9B_SETTINGS* g9bSettings)
{
	BYTE* pImageData;
	BYTE* pG9bData;
	BYTE* pPaletteData;
	BYTE  bitDepth,g9bBitDepth;
	DWORD width;
	DWORD height;
	BYTE  nrColors;
    DWORD dataSize;
	BYTE  colorType=0;
	FILE* g9bFile; 

	if(!LoadImage(fileName,&pImageData,&pPaletteData,&bitDepth,&nrColors,&width,&height)) return;
    
	// Get roi and replace original image with selection
	if(g9bSettings->roi)
	{
		if((g9bSettings->left+g9bSettings->width)>width)
		{
			cout << "*** Width and/or left out of range" << endl;
			return;
		}
		if((g9bSettings->top+g9bSettings->height)>height)
		{
			cout << "*** Height and/or top out of range" << endl;
			return;
		}
		GetRoi(g9bSettings,&pImageData,width,height,bitDepth);
	}
	// Set extention to .G9B 
	char* pStr = strrchr(fileName,'.');
	if(pStr != NULL) 
	{
		strcpy(pStr,".G9B");
	}
	else
	{
		strcat(fileName,".G9B");
	}
	// Write G9B header
	switch(bitDepth)
	{
		case 24:
			if(g9bSettings->dither == true)
			{
			    DitherImage(g9bSettings,pImageData,width,height);
			}
			if((g9bSettings->yuv == true) || (g9bSettings->yjk == true))
			{
				g9bBitDepth = 8;
				if(g9bSettings->yuv == true)
				{
					colorType = 192;
				}
				else
				{
					colorType = 128;
				}
			}
			else
			{
				g9bBitDepth=16;
			}
			break; 
		case  8: g9bBitDepth=8; break;
		case  4: g9bBitDepth=4; break;
		case  1: g9bBitDepth=2; break; 
        default: cout << "Unsuppored bit depth" << endl; return;
	}
	
	if(g9bSettings->patterns == false)
	{
		g9bFile = CreateG9bHeader(fileName,g9bBitDepth,colorType,nrColors,width,height,&dataSize);
	}
	else	
	{
		DWORD newHeight = (width / g9bSettings->patX) *  (height / g9bSettings->patY) *  g9bSettings->patY;
		g9bFile = CreateG9bHeader(fileName,g9bBitDepth,colorType,nrColors,g9bSettings->patX,newHeight,&dataSize);
	}
		
	if(g9bFile == NULL)
	{
		cout << "*** Error creating G9B file" << endl;
		return;
	}

	// Write palette
	if(nrColors > 0)
	{
		fwrite(pPaletteData,1,nrColors*3,g9bFile);
	}

	pG9bData = new BYTE[dataSize];

	switch(bitDepth)
	{
		case 24: 
				if((g9bSettings->yuv) || (g9bSettings->yjk)) 
				{ 	
					if(width&3)
					{
						cout << "***  In YUV/YJK mode width must be dividable by 4" << endl;
						return;
					}
					if(g9bSettings->yuv)
					{
						Convert24To8BitYUV411(width,height,pImageData,pG9bData);
					}
					else
					{
						Convert24To8BitYJK411(width,height,pImageData,pG9bData);
					}
				}
				else
				{
					Convert24To15Bit(width,height,pImageData,pG9bData);
				}
				break;
		case  8: 
		{
			if(!Convert256To64Color(width, height,pImageData,pG9bData))
                cout << "*** Warning image uses color number higher then 63" << endl;
			break;
		}
		case  4: memcpy(pG9bData,pImageData,dataSize);break;
		case  1: Convert1To2Bit(width, height,pImageData,pG9bData);break;
	}

	if(g9bSettings->patterns == true)
	{
		if(((g9bSettings->yuv == true) ||  (g9bSettings->yjk == true)) && ((g9bSettings->patX&3) != 0))
		{
			cout << "***  In YUV/YJK mode pattern width must be dividable by 4" << endl;
			return;
		}

		if(width%g9bSettings->patX!=0)
		{
			cout << "Pattern size not devidable by image width" << endl;
			return;
		}
		if(height%g9bSettings->patY!=0)
		{
			cout << "Pattern size not devidable by image height" << endl;
			return;
		}
		MakePatterns(pG9bData,width,height,g9bBitDepth,g9bSettings);
	}
	fwrite(pG9bData,1,dataSize,g9bFile);
	
	delete pImageData;
	delete pG9bData;
	
	if(pPaletteData != NULL)
	{
		delete pPaletteData;
	}
	fclose(g9bFile);
	if(g9bSettings->compression == true)
	{
		Compress(fileName,dataSize);
	}
}

void GetPatSize(char* patSize,DWORD* patX,DWORD* patY)
{
	if (strlen(patSize)>0)
	{
		char* strPtr=strchr(patSize,',');
		*strPtr=0;
		strPtr++;
		*patX=atoi(patSize);
		*patY=atoi(strPtr);
	}
	else
    {
	    *patX=32;
	    *patY=32;
	}
}

DWORD GetDataSize(char* fileName)
{
	FILE* file;
	G9B_HEADER g9bHeader;
	file=fopen(fileName,"rb");
	if(file)
	{
		fread(&g9bHeader,1,sizeof(G9B_HEADER),file);
		fclose(file);
		if(g9bHeader.info.compression==1)
		{
			cout << "File is already compressed" << endl;
			return 0;
		}
		return g9bHeader.info.dataSize[0] | (g9bHeader.info.dataSize[1]<<8)| (g9bHeader.info.dataSize[2]<<16);
	}
	return 0;
}


DWORD CheckFileFormat(char* fileName)
{
	FILE* file;
	char temp[3]={0};
	file=fopen(fileName,"rb");
	if(file)
	{
		fread(temp,1,3,file);
		fclose(file);
		if(strncmp("BM",temp,2)==0) return 1; // bitmap file
		if(strncmp("G9B",temp,3)==0) return 2; // G9b file			
	}
	return 0;
}


int main(int argc,char* argv[])
{
	char fileName[256]={0};
	G9B_SETTINGS g9bSettings={true,false,16,16,false,0,0,0,0,false,false,false};

	cout << "TB - Windows Bitmap to Gfx9000 bitmap converter V0.6" << endl << endl; 
   
	if (argc==1)
	{
		cout << "Usage :" << endl;
		cout << "  - bmp2g9b [d][path]filename.ext [-option]" << endl;
		cout << "    - Input File can be bmp and g9b" << endl;
		cout << " -b    = Disable bitbuster compression" << endl;
		cout << " -pX,Y = Enable bitmap pattern mode" << endl; 
		cout << "       - X = Pattern X size" << endl;
		cout << "       - Y = Pattern Y size" << endl;
		cout << " -rL,T,W,H = Roi mode" << endl; 
		cout << "       - L = Left" << endl;
		cout << "       - T = Top" << endl;
		cout << "       - W = Width" << endl;
		cout << "       - H = Height" << endl;
		cout << " -d    = Dither image" << endl;
		cout << " -y    = Enable YUV conversion for 24bit bmps" << endl;
		cout << " -J    = Enable YJK conversion for 24bit bmps" << endl  << endl;
		return 0;
	}

    // Scan command options
	for (int i = 1; i < argc; i++)
	{
		// Check on - and / for option
		if ((argv[i][0]=='-') || (argv[i][0]=='/'))
        {
		   // Get character and upercase
		   char option=argv[i][1]&0xdf;
		   switch(option)
		   {	
				 case 'B': g9bSettings.compression = false;
						   break;
				 case 'P': g9bSettings.patterns= true;
					       GetPatSize(&argv[i][2],&g9bSettings.patX,&g9bSettings.patY);
						   break;
				 case 'R': g9bSettings.roi=true;
						   if(!GetRoiSize(&argv[i][2],&g9bSettings.left,&g9bSettings.top,&g9bSettings.width,&g9bSettings.height)) return 0;
						   break;
				 case 'Y': g9bSettings.yuv=true;break;		
				 case 'J': g9bSettings.yjk=true;break;
				 case 'D': g9bSettings.dither=true;break;
				 default: cout << "*** Invalid option" << endl; 
						   return 0;
		   }		
		}
		// Otherwise the argument is the file name
		else
		{
			strcpy(fileName,argv[i]);
		}
	}
	if(strlen(fileName)==0) 
	{
		cout << "*** no file specified" << endl;
		return 0;
	}
	DWORD format=CheckFileFormat(fileName);
	if(format==1) ProcessImage(fileName,&g9bSettings);
	else if (format==2)
	{
		DWORD dataSize=GetDataSize(fileName);
		if(dataSize==0) return 0;
		Compress( fileName, dataSize );
	}
	return 0;
}